import warnings
warnings.filterwarnings('ignore')
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import tensorflow as tf
tf.compat.v1.logging.set_verbosity(40)
from tensorflow.keras.datasets import mnist
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score,confusion_matrix,roc_curve,auc,precision_score,recall_score,f1_score
import seaborn as sns

# 加载数据--以元组格式加载
(x_train,y_train),(x_test,y_test) = mnist.load_data()
# 将特征数据转换为二维数据
x_test = x_test.reshape(-1,28*28)
# 将数据类型转换为浮点型并且进行归一化
x_test = x_test.astype('float')/255

for i,y in enumerate(y_test):
    if y==5:
        y_test[i] = 1
    else:
        y_test[i] = 0

y_test = np.reshape(y_test,(-1,1))
x_train,x_test,y_train,y_test = train_test_split(x_test,y_test,train_size=0.7)

# 8)	编写一个类，需要包含：代价函数，训练函数，测试函数
class MyModel():
    def __init__(self,x_train,y_train,iter=3000,alpha=0.1,hidden_layer_num=100):
        self.x_train = x_train
        self.y_train = y_train
        self.iter = iter
        self.alpha = alpha

        self.m,self.n = x_train.shape
        self.w1 = np.random.randn(self.n,hidden_layer_num)
        self.b1 = np.random.randn(hidden_layer_num)
        self.w2 = np.random.randn(hidden_layer_num,1)
        self.b2 = np.random.randn(1)
        self.J = np.zeros(iter)

    # 9)
    def my_cost(self,h, y):
        return  -np.mean(y*np.log(h)+(1-y)*np.log(1-h))

    # 10)    正确在类中编写训练的函数，设置迭代次数为20000，学习率为0.1
    def my_train(self):
        for i in range(self.iter):
            a1,a2 = self.my_predict(self.x_train,self.w1,self.b1,self.w2,self.b2)
            self.J[i] = self.my_cost(a2, self.y_train)

            dz2 = a2 - self.y_train
            dw2 = 1/self.m*a1.T.dot(dz2)
            db2 = np.mean(dz2, axis=0)
            da1 = dz2.dot(dw2.T)
            dz1 = da1 * a1 * (1 - a1)
            dw1 = 1/self.m*self.x_train.T.dot(dz1)
            db1 = np.mean(dz1, axis=0)

            self.w1 -= self.alpha * dw1
            self.b1 -= self.alpha * db1
            self.w2 -= self.alpha * dw2
            self.b2 -= self.alpha * db2

            # 13)    每500次打印，并输出代价
            if i%500==0:
                print(f'迭代次数:{i},代价值:{self.J[i]:.5f}')

        return self.J, self.w1,self.b1,self.w2,self.b2

    # 11)    在类中编写出预测的方法
    def my_predict(self,x,w1,b1,w2,b2):
        a1 = self.my_sigmoid(x.dot(w1)+b1)
        a2 = self.my_sigmoid(a1.dot(w2)+b2)
        return a1,a2

    def my_sigmoid(self,z):
        return 1 / (1 + np.exp(-z))

if __name__ == '__main__':
    # 12)    实例化该类，并进行训练
    model = MyModel(x_train,y_train)
    J,w1,b1,w2,b2 = model.my_train()
    print(f'权重1:\n{w1},\n偏置1:\n{b1},\n权重2:\n{w2},\n偏置2:\n{b2}')

    # 14)    绘出训练的学习曲线
    plt.plot(J)
    plt.show()

    a1_test,h_test = model.my_predict(x_test,w1,b1,w2,b2)
    for i,h in enumerate(h_test):
        if h>0.5:
            h_test[i]=1
        else:
            h_test[i]=0

    acc_test = accuracy_score(y_test,h_test)
    print('准确率:',acc_test)

    confusion_matrix_test = confusion_matrix(y_test,h_test)
    print('混淆矩阵:\n',confusion_matrix_test)

    sns.heatmap(confusion_matrix_test,annot=True)
    plt.show()

    precision_score_test = precision_score(y_test,h_test)
    print('查准率:',precision_score_test)

    recall_score_test = recall_score(y_test, h_test)
    print('查全率:', recall_score_test)

    f1_score_test = f1_score(y_test,h_test)
    print('f1分值:',f1_score_test)

    a1_train, h_train = model.my_predict(x_train, w1, b1, w2, b2)
    fpr,tpr,thresholds = roc_curve(y_train,h_train)

    plt.plot(fpr,tpr)
    plt.show()

    auc_test = auc(fpr,tpr)
    print('auc值:',auc_test)